#pragma once
#include "Log.hpp"
#include <queue>
#include "Protocol.hpp"

int THREAD_NUM = 5;

class Task{
private:
    CallBack _call_back;
    int _sock = -1;
public:
    Task(){}
    Task(int sock):_sock(sock){}

    void RunTask(){
        _call_back(_sock);
    }
};

class ThreadPool{
private:
    std::queue<Task> _queue;
    int _num;
    pthread_mutex_t _mtx;
    pthread_cond_t _cond;
    static ThreadPool* _instance;
private:
    ThreadPool():_num(THREAD_NUM){
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    void WackUp(){
        pthread_cond_signal(&_cond);
    }
    void Wait(){
        pthread_cond_wait(&_cond, &_mtx);
    }
    void Lock(){
        pthread_mutex_lock(&_mtx);
    }
    void UnLock(){
        pthread_mutex_unlock(&_mtx);
    }
public:
    static ThreadPool* GetInstance(){
        if(nullptr == _instance){
            pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
            pthread_mutex_lock(&lock);
            if(nullptr == _instance){
                _instance = new ThreadPool();
                if(false == _instance->InitThreadPool()){
                    LOG(FATAL, "Init ThreadPool ERROR!");
                    exit(3);
                }
            }
            pthread_mutex_unlock(&lock);
        }
        return _instance;
    }
    bool IsEmpty(){
        return _queue.size() == 0;
    }
    void Push(const Task& task){
        Lock();
        _queue.push(task);
        WackUp();
        UnLock();
    }
    void Pop(Task* task){
        *task = _queue.front();
        _queue.pop();
    }

    static void* ThreadRoutine(void* argv){
        ThreadPool* pool_ptr = (ThreadPool*)argv;
        pthread_detach(pthread_self());
        Task task;
        while(true){
            pool_ptr->Lock();
            while(pool_ptr->IsEmpty()){
                pool_ptr->Wait();
            }
            pool_ptr->Pop(&task);
            pool_ptr->UnLock();
            task.RunTask();
        }
        LOG("INFO", "Thraed Quit!");
        return nullptr;
    }
    bool InitThreadPool(){
        for(int i = 0; i < _num; ++i){
            pthread_t pid;
            if(0 != pthread_create(&pid, nullptr, ThreadRoutine, (void*)this)){
                LOG(FATAL, "Thead Create Error!");
                exit(2);
            }
        }
        LOG(INFO, "Thread Create Success!");
        return true;
    }
    ~ThreadPool(){
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cond);
    }
};

ThreadPool* ThreadPool::_instance = nullptr;
